// hwinshell.h

#pragma once
#ifndef __HWINSHELL_H__
#define __HWINSHELL_H__

#include "hwindef.h"
#include "hwincom.h"
#include "hwinexception.h"
#include "hwinvariant.h"
#include "hwinpropsys.h"


namespace harlinn
{
    namespace windows
    {

        

        struct FilterSpecification
        {
            String name;
            String specification;
        };


        class ShellItem : public Unknown
        {
        public:
            typedef Unknown Base;
            HARLINN_WINDOWS_COM_STANDARD_METHODS_IMPL(ShellItem,Unknown,IShellItem2,IUnknown)

            template <typename T>
            T BindToHandler(IBindCtx *pbc,REFGUID bhid)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                T::InterfaceType* result = nullptr;
                auto hr = pInterface->BindToHandler(pbc,bhid,__uuidof(T::InterfaceType),&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return T(result);
            }

            ShellItem GetParent( )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IShellItem* shellItem = nullptr;
                auto hr = pInterface->GetParent(&shellItem);
                if(FAILED(hr))
                {
                    //CheckHRESULT(hr);
                    return ShellItem(); // 
                }
                IShellItem2* result = nullptr;
                hr = shellItem->QueryInterface(&result);
                shellItem->Release();
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }

                return ShellItem(result);
            }

            String GetDisplayName( SIGDN sigdnName = SIGDN_NORMALDISPLAY)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                LPWSTR result;
                auto hr = pInterface->GetDisplayName(sigdnName,&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                String s(result);
                CoTaskMemFree(result);
                return s;
            }

            SFGAOF GetAttributes( SFGAOF sfgaoMask )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                SFGAOF result;
                auto hr = pInterface->GetAttributes(sfgaoMask,&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }

            int Compare( IShellItem *psi, SICHINTF hint = SICHINT_DISPLAY)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                int result;
                auto hr = pInterface->Compare( psi, hint,&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }


            PropertyStore GetPropertyStore( GETPROPERTYSTOREFLAGS flags = GPS_DEFAULT)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IPropertyStore* result = nullptr;
                auto hr = pInterface->GetPropertyStore( flags ,__uuidof(IPropertyStore),(void**)&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return PropertyStore(result);
            }

            PropertyStore GetPropertyStoreWithCreateObject(IUnknown *punkCreateObject ,GETPROPERTYSTOREFLAGS flags = GPS_DEFAULT)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IPropertyStore* result = nullptr;
                auto hr = pInterface->GetPropertyStoreWithCreateObject( flags,punkCreateObject ,__uuidof(IPropertyStore),(void**)&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return PropertyStore(result);
            }
            PropertyStore GetPropertyStoreForKeys(const PROPERTYKEY *rgKeys, UINT cKeys, GETPROPERTYSTOREFLAGS flags = GPS_DEFAULT)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IPropertyStore* result = nullptr;
                auto hr = pInterface->GetPropertyStoreForKeys( rgKeys, cKeys, flags ,__uuidof(IPropertyStore),(void**)&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return PropertyStore(result);
            }
            PropertyDescriptionList GetPropertyDescriptionList( REFPROPERTYKEY keyType)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IPropertyDescriptionList* result = nullptr;
                auto hr = pInterface->GetPropertyDescriptionList( keyType ,__uuidof(IPropertyDescriptionList),(void**)&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return PropertyDescriptionList(result);
            }

            ShellItem& Update( IBindCtx *pbc)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Update( pbc );
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
            PropertyVariant GetProperty( REFPROPERTYKEY key)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                PropertyVariant result;
                auto hr = pInterface->GetProperty( key , &result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }
            CLSID GetCLSID( REFPROPERTYKEY key )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                CLSID result = {0,};
                auto hr = pInterface->GetCLSID( key , &result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }
            DateTime GetFileTime(REFPROPERTYKEY key)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                long long ft = {0,};
                auto hr = pInterface->GetFileTime( key , (FILETIME*)&ft);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                DateTime result = DateTime::FromFileTimeUtc(ft);
                return result;
            }
            int GetInt32( REFPROPERTYKEY key)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                int result = 0;
                auto hr = pInterface->GetInt32( key , &result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }
            String GetString( REFPROPERTYKEY key)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                LPWSTR result = nullptr;
                auto hr = pInterface->GetString( key , &result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                String s(result);
                CoTaskMemFree(result);
                return s;
            }
            ULONG GetUInt32( REFPROPERTYKEY key)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                ULONG result = 0;
                auto hr = pInterface->GetUInt32( key , &result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }
            ULONGLONG GetUInt64( REFPROPERTYKEY key)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                ULONGLONG result = 0;
                auto hr = pInterface->GetUInt64( key , &result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }
            bool GetBool( REFPROPERTYKEY key)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                BOOL result = 0;
                auto hr = pInterface->GetBool( key , &result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result != FALSE;
            }
        };


        class EnumShellItems : public Unknown
        {
        public:
            typedef Unknown Base;
            HARLINN_WINDOWS_COM_STANDARD_METHODS_IMPL(EnumShellItems,Unknown,IEnumShellItems,IUnknown)

            bool Next( ShellItem& theResult )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IShellItem* pElement = nullptr;
                ULONG actual = 0;
                auto hr = pInterface->Next(1,&pElement,&actual);
                if(actual)
                {
                    IShellItem2* pShellItem = nullptr;
                    hr = pElement->QueryInterface(&pShellItem);
                    pElement->Release();
                    if(FAILED(hr))
                    {
                        CheckHRESULT(hr);
                    }
                    theResult.Reset(pShellItem);
                    return true;
                }
                return false;
            }

            bool Next( ULONG requestedNumberOfElements, std::vector<Unknown>& result)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IShellItem** pElements = (IShellItem**)alloca(requestedNumberOfElements*sizeof(IShellItem*));
                memset(pElements,0,requestedNumberOfElements*sizeof(IShellItem*));
                ULONG actual = 0;

                auto hr = pInterface->Next(1,pElements,&actual);
                if(actual)
                {
                    result.clear();
                    for(ULONG i= 0; i < actual; i++)
                    {
                        IShellItem* pElement = pElements[i];
                        IShellItem2* pShellItem = nullptr;
                        hr = pElement->QueryInterface(&pShellItem);
                        pElement->Release();
                        if(FAILED(hr))
                        {
                            for(ULONG ii= i+1; ii < actual; ii++)
                            {
                                pElements[ii]->Release();
                            }
                            CheckHRESULT(hr);
                        }
                        result.push_back(ShellItem(pShellItem));
                    }
                    return true;
                }
                return false;
            }
            EnumShellItems& Skip(ULONG numberOfElements)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Skip(numberOfElements);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }
        
            EnumShellItems& Reset( )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                auto hr = pInterface->Reset();
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return *this;
            }

            EnumShellItems Clone()
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IEnumShellItems* pClone = nullptr;
                auto hr = pInterface->Clone(&pClone);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return EnumShellItems(pClone);
            }

        };


        class ShellItemArray : public Unknown
        {
        public:
            typedef Unknown Base;
            HARLINN_WINDOWS_COM_STANDARD_METHODS_IMPL(ShellItemArray,Unknown,IShellItemArray,IUnknown)

            template <typename T>
            T BindToHandler(IBindCtx *pbc,REFGUID bhid)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                T::InterfaceType* result = nullptr;
                auto hr = pInterface->BindToHandler(pbc,bhid,__uuidof(T::InterfaceType),&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return T(result);
            }

            PropertyStore GetPropertyStore( GETPROPERTYSTOREFLAGS flags = GPS_DEFAULT)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IPropertyStore* result = nullptr;
                auto hr = pInterface->GetPropertyStore( flags ,__uuidof(IPropertyStore),(void**)&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return PropertyStore(result);
            }

            PropertyDescriptionList GetPropertyDescriptionList( REFPROPERTYKEY keyType)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IPropertyDescriptionList* result = nullptr;
                auto hr = pInterface->GetPropertyDescriptionList( keyType ,__uuidof(IPropertyDescriptionList),(void**)&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return PropertyDescriptionList(result);
            }

        
            SFGAOF GetAttributes( SIATTRIBFLAGS AttribFlags,SFGAOF sfgaoMask)
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                SFGAOF result;
                auto hr = pInterface->GetAttributes(AttribFlags,sfgaoMask,&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }
            DWORD GetCount( )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                DWORD result;
                auto hr = pInterface->GetCount(&result);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return result;
            }
        
            ShellItem GetItemAt( DWORD dwIndex )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IShellItem* pElement = nullptr;
                auto hr = pInterface->GetItemAt(dwIndex,&pElement);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }

                IShellItem2* pShellItem = nullptr;
                hr = pElement->QueryInterface(&pShellItem);
                pElement->Release();
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return ShellItem(pShellItem);
            }
        
            EnumShellItems EnumItems( )
            {
                HWIN_TRACE();
                auto pInterface = GetInterface();
                IEnumShellItems* pEnumItems = nullptr;
                auto hr = pInterface->EnumItems(&pEnumItems);
                if(FAILED(hr))
                {
                    CheckHRESULT(hr);
                }
                return EnumShellItems(pEnumItems);
            }

        };

    };
};

#endif // __HWINSHELL_H__